﻿#SingleInstance, Force
SendMode Input
SetWorkingDir, %A_ScriptDir%
SetBatchLines, -1
SetKeyDelay, -1
SetMouseDelay, -1
DetectHiddenWindows, Off
Menu, Tray, NoStandard
Menu, Tray, DeleteAll
Menu, Tray, Add, 省吾, ShowOrHide
Menu, Tray, Add, 重启&r, 重启
Menu, Tray, Add, 退出&q, 退出
Menu, Tray, Default, 省吾
Menu, Tray, Tip, 吾日三省吾身`,每天都在前进的路上
Menu, Tray, Click, 2


;************************************* 读入配置文件      ***********************
初始=
(
[好好吃饭]
创建日期=2022-08-28
紧急程度=紧急
截止日期=20220829
倒计时=
简介=
完成情况=0
)
IfNotExist,省吾.ini
{
省吾:= class_EasyIni("省吾.ini",初始)
省吾.save()
}
省吾:= class_EasyIni("省吾.ini")


;************************************* 搜索框相关按键和变量      ***********************
Hotkey, if, (WinExist("ahk_id " MyGuiHwnd))
Hotkey,enter,框子添加
Hotkey, if
框体位置x := 1250
框体位置y := 600
框体位置w := 800
字号 := 16
字体 := "微软雅黑"
;*************************************       ***********************




;*************************************   主程序    ***********************
Gosub,主程序
Return

主程序:
gui swMain:Destroy
; gui swMain:add,Tab3,, 任务
; Gui,swMain:Tab,1
Gui swMain:Default 
Gui swMain: +Hwndsw +LastFound +AlwaysOnTop
Gui swMain:Font, s12
Gui swMain:Add, ListView,-Multi Grid Count10 HwndHLV xs y+5 AltSubmit Checked w880 h600 cred gifCheck, 标题|紧急程度|截止日期|倒计时|简介
LV_ModifyCol(1,300) ,LV_ModifyCol(2,80) , LV_ModifyCol(3,130) ,LV_ModifyCol(4,130) , LV_ModifyCol(5,300)
Gosub, 更新任务倒计时
Gosub, 读入项目
gui swMain:Show,w900 h620,省吾--一个可以替代滴答清单的效率工具
Return
^!s::Gosub,主程序

ShowOrHide:
If (WinExist("ahk_id" sw))
	gui swMain:Destroy
Else
	gui swMain:Show,w900 h620,省吾--一个可以替代滴答清单的效率工具
Return

swMainGuiClose:
swMainGuiEscape:
gui swMain:Destroy
Return
重启:
reload
Return
退出:
ExitApp 
return


;*************************************  添加框子部分     ***********************


^!n::
If (WinExist("ahk_id " MyGuiHwnd))
	{
        Gosub, destory
        Return
	}
Gui, add:Margin, 0, 0
Gui, add:-Caption +Border
Gui, add:+AlwaysOnTop -DPIScale +ToolWindow +HwndMyGuiHwnd +E0x02000000 +E0x00080000
Gui, add:Font, s%字号%, %字体%
gui,add:add,Text,Section,待办
Gui, add:Add, Edit,x+5  v标题 HwndSSK w%框体位置w% -E0x200 h40 w500
gui,add:add,Text,xs y+5,简介
gui, add:add, edit,x+5 w500 v简介
gui,add:add,Text,xs y+5,截止
Gui, add:Add, DateTime,x+5 v截止日期 w400, LongDate
Gui, add:Add, Button,x+5 g框子添加,添加待办
Gui, add:Font, s%字号%, %字体%
gui, add:Submit,NoHide
Gui, add:show, AutoSize x%框体位置x% y%框体位置y%
Gosub, 更新任务倒计时
Return
destory:
Gui, add:Destroy
Return


框子添加:
gui add:Submit,NoHide
Gosub, 写入待办
Gosub, destory
ToolTip,添加待办成功
SetTimer, removetooltip, 1000
Return
removetooltip:
ToolTip
Return

写入待办:
创建日期:=A_Now
if (RegExMatch(标题, "!|！")){
    紧急程度:="紧急"
    标题:=RegExReplace(标题, "!|！")
}
if (截止日期!=""){
	差额:=time.minus(,截止日期)
	If (差额>0)
		倒计时:=差额 . "天到期"
	else if (差额=0)
		倒计时:="今天到期"
	Else
		倒计时:="超时" . 差额 . "天"
截止日期:=time.get(1,截止日期)
标题.=A_now
省吾.AddSection(标题,"创建日期",创建日期)
省吾[标题]["紧急程度"]:=紧急程度
省吾[标题]["截止日期"]:=截止日期
省吾[标题]["倒计时"]:=倒计时
省吾[标题]["简介"]:=简介
省吾[标题]["完成情况"]:=0
省吾.save()
}
创建日期:=紧急程度:=截止日期:=倒计时:=""
Gosub, ifCheck
Return
addGuiClose:
addGuiEscape:
gui add:Destroy
Return

#If (WinExist("ahk_id " MyGuiHwnd))
#If

更新任务倒计时:
for sec,kv in 省吾{
	差额:=time.minus(,省吾[sec]["截止日期"])
	If (差额>0)
		倒计时:=差额 . "天到期"
	else if (差额=0)
		倒计时:="今天到期"
	Else
		倒计时:="超时" . 差额 . "天"
	省吾[sec]["倒计时"]:=倒计时
	省吾.save()
	创建日期:=紧急程度:=截止日期:=倒计时:=""
}

Return


ifCheck:
; loop % 省吾.Count(){
for sec,kv in 省吾{
	if (ExtLV.GetItemState(A_Index,HLV)["checked"]!=0){
		; MsgBox,% ExtLV.GetSubItemText(A_index, 2,HLV)
			secname:=ExtLV.GetSubItemText(A_index, 1,HLV)
			; LV_Delete(secname)
			for sec,kv in 省吾{
				secOnly:=RegExReplace(sec, "\d{8,16}")
				; if (RegExMatch(sec,secname) && secname!=""){
				if (secOnly=secname) and (省吾[sec]["完成情况"]!=1){
					省吾[sec]["完成情况"]:=1
					省吾[sec]["完成时间"]:=A_Now
					省吾.save()
					创建日期:=紧急程度:=截止日期:=倒计时:=简介:=secname:=""
					break
				}
			}
	}
}
LV_Delete()
Gosub, 读入项目
Return

读入项目:
for sec,kv in 省吾{
	if (省吾[sec]["完成情况"]!=1){
	secOnly:=RegExReplace(sec, "\d{8,16}") , 截止日期:=time.get(4,省吾[sec]["截止日期"])
	LV_Add(,secOnly,省吾[sec]["紧急程度"],截止日期,省吾[sec]["倒计时"],省吾[sec]["简介"])
	}
}
Return

class time
{
get(mode:=1,str:=""){ ;space the second parameter equal to use A_now
global outTime
    switch mode{
    case 1:
    FormatTime, outTime,%str%,yyyyMMdd
    case 2:
    FormatTime, outTime,%str%,yyyy-MM-dd
    case 3:
    FormatTime, outTime,%str%,yyyy.MM.dd
    case 4:
    FormatTime, outTime,%str%,yyyy年MM月dd日
    case 5:
    FormatTime, outTime,%str%,yyyy年MM月dd日HH点mm分ss秒
    }
Return outTime
}
minus(day1:="",day2:=""){
if (day1=""){
    FormatTime, day1,,yyyyMMdd
}
EnvSub,day2,%day1%,days
Return day2
}
plus(day:="",number:=0){
    if (day=""){
    FormatTime, day,,yyyyMMdd
}
    day +=number,Days
    Return day
}
}








Class ExtLV
{
	__New(HWND){
		this.HWND:=HWND
		Return this
	}
	;全选所有行，flag=1全选，反之
	SelectAll(flag:=True,HWND:=0){
		Static LVM_SETITEMSTATE := 0x1000 + 43
		HLV:=HWND?HWND:this.HWND?this.HWND:0
		VarSetCapacity(LVITEM, 4*15, 0)
		NumPut(0x00000008, LVITEM, 4*0) ;LVIF_STATE
		NumPut(flag?0x00000002:0x00000000, LVITEM, 4*3) ;state
		NumPut(0x0002, LVITEM, 4*4) ;LVIS_SELECTED
		DllCall("User32\SendMessage", "Ptr", HLV, "UInt", LVM_SETITEMSTATE, "Ptr", -1, "Ptr", &LVITEM)
		ControlFocus, SysListview321
	}
	;获取目标ListView选择的行列位置信息
	GetItemHitPos(ByRef Row, ByRef Column,HWND:=0,Xpos:=-1, Ypos:=-1) {
		Static LVM_SUBITEMHITTEST := 0x1039
		Static LVHT_ONITEM := 0x0000000E
		HLV:=HWND?HWND:this.HWND?this.HWND:0
		if !HLV
			Return {}
		VarSetCapacity(LVHTI, 24, 0)
		if (Xpos = -1) || (Ypos = -1){
			DllCall("User32.dll\GetCursorPos", "Ptr", &LVHTI)
			DllCall("User32.dll\ScreenToClient", "Ptr", HLV, "Ptr", &LVHTI)
		}Else{
			NumPut(Xpos, LVHTI, 0, "Int")
			NumPut(Ypos, LVHTI, 4, "Int")
		}
		r:=DllCall("User32\SendMessage", "Ptr", HLV, "UInt", LVM_SUBITEMHITTEST, "Ptr", 0, "Ptr", &LVHTI)
		Row:= r > 0x7FFFFFFF ? 0 : r + 1
		Column:=(NumGet(LVHTI, 8, "UInt") & LVHT_ONITEM) ? NumGet(LVHTI, 16, "Int") + 1 : 0
		Return {Row:Row,Column:Column}
	}
	;获取目标ListView指定行列单元格内容
	GetSubItemText(Row, Column :=1,HWND:=0, MaxChars := 257){  ;GetSubItemText(A_index, 2,HLV)
		Static LVM_GETITEMTEXTW:=0x1073,LVM_GETITEMTEXTA:=0x102D
		Static LVM_GETITEMTEXT := A_IsUnicode ? LVM_GETITEMTEXTW : LVM_GETITEMTEXTA
		Static OffText := 16 + A_PtrSize
		Static OffTextMax := OffText + A_PtrSize
		HLV:=HWND?HWND:this.HWND?this.HWND:0
		if !HLV
			Return ""
		VarSetCapacity(ItemText, MaxChars << !!A_IsUnicode, 0)
		VarSetCapacity(LVITEM, 48 + (A_PtrSize * 3), 0)
		NumPut(0, LVITEM, 0, "UInt"), NumPut(Row - 1, LVITEM, 4, "Int")
		, NumPut(Column - 1, LVITEM, 8, "Int")
		NumPut(&ItemText, LVITEM, OffText, "Ptr")
		NumPut(MaxChars, LVITEM, OffTextMax, "Int")
		DllCall("User32\SendMessage", "Ptr", HLV, "UInt", LVM_GETITEMTEXT, "Ptr", Row - 1, "Ptr", &LVITEM)
		VarSetCapacity(ItemText, -1)
		Return ItemText
	}
	;获取目标ListView指定行状态
	GetItemState(Row,HWND:=0) {
		Static LVM_GETITEMSTATE:= 0x102C
		Static LVIS := {Cut: 0x04, DropHilited: 0x08, Focused: 0x01, Selected: 0x02, Checked: 0x2000}
		HLV:=HWND?HWND:this.HWND?this.HWND:0
		States :=DllCall("User32\SendMessage", "Ptr", HLV, "UInt", LVM_GETITEMSTATE, "Ptr", Row - 1, "Ptr", 0xFFFF, "Ptr")
		Result := {}
		For Key, Value In LVIS
			Result[Key] := States & Value
		Return Result
	}
	;修改listview指定单元格内容
	SetText(Text, Row, Col:=1,HWND:=0) {
		Static LVM_SETITEMTEXTA := 0x102E, LVM_SETITEMTEXTW := 0x1074
		Static LVM_SETITEMTEXT := A_IsUnicode ? LVM_SETITEMTEXTW : LVM_SETITEMTEXTA
		HLV:=HWND?HWND:this.HWND?this.HWND:0
		VarSetCapacity(LVITEM, 48 + (A_PtrSize * 3), 0)
		NumPut(0, LVITEM, 0, "UInt"), NumPut(Row - 1, LVITEM, 4, "Int"), NumPut(Col - 1, LVITEM, 8, "Int")
		NumPut(&Text, LVITEM, 16 + A_PtrSize, "UPtr") ; <<<<< changed "Int" to "UPtr"
		Return DllCall("User32\SendMessage", "Ptr", HLV, "UInt", LVM_SETITEMTEXT, "Ptr", Row - 1, "Ptr", &LVITEM, "UInt")
	}
	;获取目标ListView中的下一项
	GetNextItem(Row,HWND:=0) {
		Static LVM_GETNEXTITEM := 0x100C
		HLV:=HWND?HWND:this.HWND?this.HWND:0
		Return DllCall("User32\SendMessage", "Ptr", HLV, "UInt", LVM_GETNEXTITEM, "Ptr", Row, "Ptr", 0)
	}
	;设置listview背景图片
	SetBackImage(ImgPath,HWND:=0) {
		Static LVM_SETBKIMAGEA := 0x1044
		Static OSVERSION := DllCall("Kernel32.dll\GetVersion", "UInt") & 0xFF
		Static LVS_EX_DOUBLEBUFFER:=0x00010000
		Static LVBKIF_TYPE_WATERMARK:=0x10000000
		HLV:=HWND?HWND:this.HWND?this.HWND:0
		if !HLV
			Return False
		HBITMAP:=LoadPicture(ImgPath,"GDI+")
		; 设置扩展样式LVS_EX_DOUBLEBUFFER以避免绘图问题
		this.SetExtendedStyle(LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER,HLV) 
		If (HBITMAP) && (OSVERSION >= 6)
			LVBKIF_TYPE_WATERMARK |= 0x20000000 ; LVBKIF_FLAG_ALPHABLEND
		LVBKIMAGESize :=  A_PtrSize = 8 ? 40 : 24
		VarSetCapacity(LVBKIMAGE, LVBKIMAGESize, 0)
		NumPut(LVBKIF_TYPE_WATERMARK, LVBKIMAGE, 0, "UInt")
		NumPut(HBITMAP, LVBKIMAGE, A_PtrSize, "UPtr")
		Return DllCall("User32\SendMessage", "Ptr", HLV, "UInt", LVM_SETBKIMAGEA, "Ptr", 0, "Ptr", &LVBKIMAGE)
	}
	GetExtendedStyle(HWND:=0) {
		Static LVM_GETEXTENDEDLISTVIEWSTYLE := 0x1037
		HLV:=HWND?HWND:this.HWND?this.HWND:0
		Return DllCall("User32\SendMessage", "Ptr", HLV, "UInt", LVM_GETEXTENDEDLISTVIEWSTYLE, "Ptr", 0, "Ptr", 0, "Ptr")
	}
	SetExtendedStyle(StyleMsk, Styles,HWND:=0) {
		Static LVM_SETEXTENDEDLISTVIEWSTYLE := 0x1036
		HLV:=HWND?HWND:this.HWND?this.HWND:0
		Return DllCall("User32\SendMessage", "Ptr", HLV, "UInt", LVM_SETEXTENDEDLISTVIEWSTYLE, "Ptr", StyleMsk, "Ptr", Styles, "Ptr")
	}
	;选中listview指定行
	ClickRow(Row,HWND:=0){
		Static LVM_GETITEMRECT:=0x100E
		Static WM_LBUTTONDOWN:=0x0201
		Static WM_LBUTTONUP:=0x0202
		HLV:=HWND?HWND:this.HWND?this.HWND:0
		VarSetCapacity(RECT, 16, 0)
		DllCall("User32\SendMessage", "Ptr", HLV, "UInt", LVM_GETITEMRECT, "Ptr", Row - 1, "Ptr", &RECT)
		POINT := NumGet(RECT, 0, "Short") | (NumGet(RECT, 4, "Short") << 16)
		DllCall("user32\PostMessage", "ptr", HLV, "uint", WM_LBUTTONDOWN, "ptr", 0, "ptr", POINT)
		DllCall("user32\PostMessage", "ptr", HLV, "uint", WM_LBUTTONUP, "ptr", 0, "ptr", POINT)
	}
	;获取目标ListView指定列宽
	GetColumnWidth(Column,HWND:=0) {
		Static LVM_GETCOLUMNWIDTH:= 0x101D
		HLV:=HWND?HWND:this.HWND?this.HWND:0
		Return DllCall("User32\SendMessage", "Ptr", HLV, "UInt", LVM_GETCOLUMNWIDTH, "Ptr", Column - 1, "Ptr", 0, "Ptr")
	}
	;计算显示指定行所需的大约宽度和高度
	CalcViewRect(Rows:=0,HWND:=0) {
		Static LVM_APPROXIMATEVIEWRECT := 0x1040
		HLV:=HWND?HWND:this.HWND?this.HWND:0
		Rect:=DllCall("User32\SendMessage", "Ptr", HLV, "UInt", LVM_APPROXIMATEVIEWRECT, "Ptr", Rows - 1, "Ptr", 0, "Ptr")
		Return {W: (Rect & 0xFFFF), H: (Rect >> 16) & 0xFFFF}
	}
}
#Include, class_easyini.ahk